<?php

/**
 * This file is part of Switcher.
 * 
 * Switcher is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 * 
 * Switcher is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with Switcher. If not, see <http://www.gnu.org/licenses/>.
 * 
 * @license http://www.opensource.org/licenses/gpl-3.0.html GPL-3.0
 */

namespace switcher\utils;

if ( !defined('FNM_LEADING_DIR') )
{
	define('FNM_LEADING_DIR', 8);
}

/**
 * Utility class for file-related operations.
 * 
 * @author Dri <switcher.framework@gmail.com>
 * @license http://www.opensource.org/licenses/gpl-3.0.html GPL-3.0
 * @package switcher\utils
 * @since 1.0.0
 */
final class FileHelper
{
	/**
	 * Hidden constructor
	 */
	private function __construct()
	{
	}
	
	/**
	 * Load a file as a string and optionally indents it.
	 * 
	 * @param string $path The path to the file
	 * @param string $indentation A blank string
	 * @return string The file content
	 */
	public static function loadFile($path, $indentation = '')
	{
		$lines = @file($path);
		return $lines === false ? false : self::indentString(implode('', $lines), $indentation);
	}
	
	/**
	 * Indent a string with a blank prefix. If the string is empty, an empty string is returned.
	 * If the last line of the string is empty, it won't be indented.
	 * 
	 * @param string $string The string to indent
	 * @param string $indentation A blank string with only spaces or tabulations
	 * @return string The indented string
	 * @throws \InvalidArgumentException If $indentation isn't a blank string
	 */
	public static function indentString($string, $indentation)
	{
		if (!is_string($indentation) || preg_match('~^[\\t ]*$~', $indentation) == 0)
		{
			throw new \InvalidArgumentException('Indentation must be a blank string : ' . gettype($indentation));
		}
		
		if ($string === '' || $indentation === '')
		{
			return $string;
		}
		
		return preg_replace('~^|\\n(?!$)~', '$0' . $indentation, $string);
	}
	
	/**
	 * Checks if the passed string would match the given shell wildcard pattern. If the <i>FNM_PATHNAME</i> flag
	 * is used, the ** pattern can be used to match through directories. Other than that, this method behaves
	 * just like the fnmatch except that it doesn't return true if there is a match. Instead it returns an array
	 * containing the values matching the wildcards.
	 * 
	 * @param string $pattern The shell wildcard pattern
	 * @param string $string The tested string
	 * @param int $flags The value of flags can be any combination of the fnmatch flags
	 * @return mixed An array filled with the values of the wildcards if there is a match, false otherwise
	 * @see fnmatch()
	 */
	public static function match($pattern, $string, $flags = 0)
	{
		if ($flags & FNM_PERIOD && $string[0] == '.' && $pattern[0] != '.') {
			return false;
		} 
		
		$regexp = self::shellToRegexp($pattern, $flags);
		
		if (preg_match($regexp, $string, $matches) == 0)
		{
			return false;
		}
		
		return $matches;
	}
	
	/**
	 * Convert a shell pattern to an equivalent regexp pattern capable of extracting matches.
	 * 
	 * @param string $pattern The shell wildcard pattern
	 * @param string $flags The fnmatch flags
	 * @return string The regexp pattern
	 */
	private static function shellToRegexp($pattern, $flags)
	{
		$isPathName = $flags & FNM_PATHNAME;
		$noEscape = $flags & FNM_NOESCAPE;
		
		$i = 0;
		$n = strlen($pattern);
		$regexp = '';
		
		while ($i < $n)
		{
			$c = $pattern[$i++];
			
			if ($c == '*')
			{
				if ($i < $n && $isPathName && $pattern[$i] == '*')
				{
					$regexp .= '(.*)';
					$i++;
				}
				else
				{
					$regexp .= $isPathName ? '([^/]*)' : '(.*)';
				}
			}
			elseif ($c == '?')
			{
				$regexp .= '(.)';
			}
			elseif ($c == '[')
			{
				$j = $i;
				
				if ($j < $n && $pattern[$j] == '!')
				{
					$j++;
				}
				
				if ($j < $n && $pattern[$j] == ']')
				{
					$j++;
				}
				
				while ($j < $n && $pattern[$j] != ']')
				{
					$j++;
					if (!$noEscape && $pattern[$j] == ']' && $pattern[$j-1] == '\\')
					{
						$j++;
					}
				}
				
				if ($j >= $n)
				{
					$regexp .= '\\[';
				}
				else
				{
					if ($noEscape)
					{
						$content = str_replace('\\', '\\\\', substr($pattern, $i, $j - $i));
					}
					else
					{
						$content = preg_replace('~\\\\(?!\\])~', '\\\\', substr($pattern, $i, $j - $i));
					}
					$i = $j + 1;
					
					if ($content['0'] == '!')
					{
						$content = '^' . substr($content, 1);
					}
					elseif ($content['0'] == '^')
					{
						$content = '\\' . $content;
					}
					
					$regexp .= '([' . $content . '])';
				}
			}
			elseif ($c == '\\' && $noEscape)
			{
				$regexp .= $c;
			}
			else
			{
				$regexp .= preg_quote($c, '~');
			}
		}
		
		$leadingDir = $flags & FNM_LEADING_DIR ? '[/]*' : '';
		$caseFlag = $flags & FNM_CASEFOLD ? 'i' : '';
		
		return '~^' . $regexp . $leadingDir . '$~' . $caseFlag;
	}
}

?>